﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Zinnia.Lite.Untilty;

namespace Zinnia.Lite.Core.Imp
{
    /// <summary>
    /// 提供平台所有组件类型
    ///引用<see cref="AssemblyNames"/> 程序集下的所有类型.
    /// </summary>
    public class AppDomainTypeService : ITypeService
    {
        #region Fields
        private bool ignoreReflectionErrors = true;
        private bool loadAppDomainAssemblies = true;
        private string assemblySkipLoadingPattern = "^System|^mscorlib|^Microsoft|^AjaxControlToolkit|^Antlr3|^Autofac|^AutoMapper|^Castle|^ComponentArt|^CppCodeProvider|^DotNetOpenAuth|^EntityFramework|^EPPlus|^FluentValidation|^ImageResizer|^itextsharp|^log4net|^MaxMind|^MbUnit|^MiniProfiler|^Mono.Math|^MvcContrib|^Newtonsoft|^NHibernate|^nunit|^Org.Mentalis|^PerlRegex|^QuickGraph|^Recaptcha|^Remotion|^RestSharp|^Rhino|^Telerik|^Iesi|^TestDriven|^TestFu|^UserAgentStringLibrary|^VJSharpCodeProvider|^WebActivator|^WebDev|^WebGrease";
        private string assemblyRestrictToLoadingPattern = ".*";
        private IList<string> assemblyNames = new List<string>();
        #endregion

        #region Ctor
        /// <summary>创建提供者的新实例</summary>
        public AppDomainTypeService()
        {
        }

        #endregion

        #region Properties
        /// <summary>
        /// 应用程序域
        /// </summary>
        public virtual AppDomain App
        {
            get { return AppDomain.CurrentDomain; }
        }

        /// <summary>
        /// 是否加载应用程序域程序集，默认加载
        /// </summary>
        public bool LoadAppDomainAssemblies
        {
            get { return loadAppDomainAssemblies; }
            set { loadAppDomainAssemblies = value; }
        }

        /// <summary>
        /// 应用程序域下的所有程序集.
        /// </summary>
        public IList<string> AssemblyNames
        {
            get { return assemblyNames; }
            set { assemblyNames = value; }
        }

        /// <summary>
        /// 程序集加载规则.
        /// </summary>
        public string AssemblySkipLoadingPattern
        {
            get { return assemblySkipLoadingPattern; }
            set { assemblySkipLoadingPattern = value; }
        }

        /// <summary>
        /// 加载程序集规则
        /// </summary>
        /// <remarks>
        /// 支持正则表达式 如^System|^mscorlib
        /// </remarks>
        public string AssemblyRestrictToLoadingPattern
        {
            get { return assemblyRestrictToLoadingPattern; }
            set { assemblyRestrictToLoadingPattern = value; }
        }
        #endregion

        #region Methods
        /// <summary>
        /// 在工程下检索类型
        /// </summary>
        /// <typeparam name="T">搜索类型</typeparam>
        /// <param name="onlyConcreteClasses">是否是具体类</param>
        /// <returns>查找到类型集合</returns>
        public IEnumerable<Type> FindClassesOfType<T>(bool onlyConcreteClasses = true)
        {
            return FindClassesOfType(typeof(T), onlyConcreteClasses);
        }
        /// <summary>
        /// 根据类型检索
        /// </summary>
        /// <param name="assignTypeFrom">搜索类型</param>
        /// <param name="onlyConcreteClasses">是否是具体类</param>
        /// <returns>查找到类型集合</returns>
        public IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, bool onlyConcreteClasses = true)
        {
            return FindClassesOfType(assignTypeFrom, GetAssemblies(), onlyConcreteClasses);
        }
        /// <summary>
        /// 根据类型检索
        /// </summary>
        /// <typeparam name="T">搜索类型</typeparam>
        /// <param name="assemblies">指定搜索程序集</param>
        /// <param name="onlyConcreteClasses">是否是具体类</param>
        /// <returns>查找到类型集合</returns>
        public IEnumerable<Type> FindClassesOfType<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true)
        {
            return FindClassesOfType(typeof(T), assemblies, onlyConcreteClasses);
        }
        /// <summary>
        /// 根据类型检索
        /// </summary>
        /// <param name="assignTypeFrom">搜索类型</param>
        /// <param name="assemblies">指定搜索程序集</param>
        /// <param name="onlyConcreteClasses">是否是具体类</param>
        /// <returns>查找到类型集合</returns>
        public IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true)
        {
            var result = new List<Type>();
            try
            {
                foreach (var a in assemblies)
                {
                    Type[] types = null;
                    try
                    {
                        types = a.GetTypes();
                    }
                    catch
                    {
                        //Entity Framework 6 doesn't allow getting types (throws an exception)
                        if (!ignoreReflectionErrors)
                        {
                            throw;
                        }
                    }
                    if (types != null)
                    {
                        foreach (var t in types)
                        {
                            if (assignTypeFrom.IsAssignableFrom(t) || (assignTypeFrom.IsGenericTypeDefinition && DoesTypeImplementOpenGeneric(t, assignTypeFrom)))
                            {
                                if (!t.IsInterface)
                                {
                                    if (onlyConcreteClasses)
                                    {
                                        if (t.IsClass && !t.IsAbstract)
                                        {
                                            result.Add(t);
                                        }
                                    }
                                    else
                                    {
                                        result.Add(t);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (ReflectionTypeLoadException ex)
            {
                var msg = ex.LoaderExceptions.Aggregate(string.Empty, (current, e) => current + (e.Message + Environment.NewLine));
                var fail = new Exception(msg, ex);
                Debug.WriteLine(fail.Message, fail);
                throw fail;
            }
            return result;
        }

        /// <summary>
        /// 获取应用程序下所有程序集
        /// </summary>
        /// <returns>程序集集合</returns>
        public virtual IList<Assembly> GetAssemblies()
        {
            string key = "AppDomainTypeFinder_GetAssemblies";
            var assemblies = HashCacheHelper.Instance.GetCache<List<Assembly>>(key);
            if (null != assemblies)
                return (List<Assembly>)assemblies;
            assemblies = new List<Assembly>();
            var addedAssemblyNames = new List<string>();
            if (LoadAppDomainAssemblies)
                AddAssembliesInAppDomain(addedAssemblyNames, assemblies);
            AddConfiguredAssemblies(addedAssemblyNames, assemblies);
            HashCacheHelper.Instance.AddCache<List<Assembly>>(assemblies, key);
            return assemblies;
        }

        #endregion

        #region Utilities
        /// <summary>
        /// 添加域名下所有匹配的程序集
        /// </summary>
        /// <param name="addedAssemblyNames"></param>
        /// <param name="assemblies"></param>
        private void AddAssembliesInAppDomain(List<string> addedAssemblyNames, List<Assembly> assemblies)
        {
            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (Matches(assembly.FullName))
                {
                    if (!addedAssemblyNames.Contains(assembly.FullName))
                    {
                        assemblies.Add(assembly);
                        addedAssemblyNames.Add(assembly.FullName);
                    }
                }
            }
        }

        /// <summary>
        ///加载程序集
        /// </summary>
        /// <param name="addedAssemblyNames"></param>
        /// <param name="assemblies"></param>
        protected virtual void AddConfiguredAssemblies(List<string> addedAssemblyNames, List<Assembly> assemblies)
        {
            foreach (string assemblyName in AssemblyNames)
            {
                Assembly assembly = Assembly.Load(assemblyName);
                if (!addedAssemblyNames.Contains(assembly.FullName))
                {
                    assemblies.Add(assembly);
                    addedAssemblyNames.Add(assembly.FullName);
                }
            }
        }

        /// <summary>
        /// 检查程序集是否符合加载规则
        /// </summary>
        /// <param name="assemblyFullName">
        /// 程序集名称
        /// </param>
        /// <returns>
        /// 匹配结果
        /// </returns>
        public virtual bool Matches(string assemblyFullName)
        {
            return !Matches(assemblyFullName, AssemblySkipLoadingPattern)
                   && Matches(assemblyFullName, AssemblyRestrictToLoadingPattern);
        }

        /// <summary>
        ///  检查程序集是否符合加载规则
        /// </summary>
        /// <param name="assemblyFullName">
        /// 程序集名称
        /// </param>
        /// <param name="pattern">
        /// 匹配规则
        /// </param>
        /// <returns>
        /// 匹配结果
        /// </returns>
        protected virtual bool Matches(string assemblyFullName, string pattern)
        {
            return Regex.IsMatch(assemblyFullName, pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
        }

        /// <summary>
        /// 加载指定目录下的dll程序集
        /// </summary>
        /// <param name="directoryPath">
        /// 指定目录
        /// </param>
        protected virtual void LoadMatchingAssemblies(string directoryPath)
        {
            var loadedAssemblyNames = GetAssemblies().Select(a => a.FullName).ToList();
            if (!Directory.Exists(directoryPath))
            {
                return;
            }
            foreach (string dllPath in Directory.GetFiles(directoryPath, "*.dll"))
            {
                try
                {
                    var an = AssemblyName.GetAssemblyName(dllPath);
                    if (Matches(an.FullName) && !loadedAssemblyNames.Contains(an.FullName))
                    {
                        App.Load(an);
                    }
                    //old loading stuff
                    //Assembly a = Assembly.ReflectionOnlyLoadFrom(dllPath);
                    //if (Matches(a.FullName) && !loadedAssemblyNames.Contains(a.FullName))
                    //{
                    //    App.Load(a.FullName);
                    //}
                }
                catch (BadImageFormatException ex)
                {
                    Trace.TraceError(ex.ToString());
                }
            }
        }

        /// <summary>
        /// 是否为泛型?
        /// </summary>
        /// <param name="type"></param>
        /// <param name="openGeneric"></param>
        /// <returns></returns>
        protected virtual bool DoesTypeImplementOpenGeneric(Type type, Type openGeneric)
        {
            try
            {
                var genericTypeDefinition = openGeneric.GetGenericTypeDefinition();
                return (from implementedInterface in type.FindInterfaces((objType, objCriteria) => true, null) where implementedInterface.IsGenericType select genericTypeDefinition.IsAssignableFrom(implementedInterface.GetGenericTypeDefinition())).FirstOrDefault();
            }
            catch
            {
                return false;
            }
        }

        #endregion
    }
}
